from typing import Dict, List, Optional
from sqlalchemy.orm import Session
from app.models.database.models import ServiceConfig, HealthCheck, AlertRule
from app.services.health.health_checker import health_checker
import logging
from datetime import datetime, timedelta

logger = logging.getLogger(__name__)


class FailureDetector:
    """故障检测服务"""

    def __init__(self):
        self.failure_threshold = 3  # 连续失败次数阈值
        self.recovery_threshold = 2  # 恢复确认次数
        self.failure_window = timedelta(minutes=5)  # 故障检测时间窗口

    def analyze_service_health(
            self,
            service_id: int,
            db: Session,
            recent_checks: int = 5
    ) -> Dict[str, any]:
        """分析服务健康状态"""

        # 获取最近的健康检查记录
        recent_health_checks = db.query(HealthCheck) \
            .filter(HealthCheck.service_id == service_id) \
            .order_by(HealthCheck.checked_at.desc()) \
            .limit(recent_checks) \
            .all()

        if not recent_health_checks:
            return {
                "status": "unknown",
                "reason": "No health check data",
                "recommendation": "Perform initial health check"
            }

        # 分析健康状态
        total_checks = len(recent_health_checks)
        healthy_checks = sum(1 for check in recent_health_checks
                             if check.status == "healthy")
        failed_checks = total_checks - healthy_checks

        # 计算失败率
        failure_rate = failed_checks / total_checks if total_checks > 0 else 0

        # 获取最新状态
        latest_check = recent_health_checks[0]

        analysis = {
            "total_checks": total_checks,
            "healthy_checks": healthy_checks,
            "failed_checks": failed_checks,
            "failure_rate": failure_rate,
            "latest_status": latest_check.status,
            "latest_check_time": latest_check.checked_at,
            "average_response_time": self._calculate_avg_response_time(recent_health_checks)
        }

        # 判断是否需要切换
        should_switch, reason = self._should_trigger_switch(
            service_id, analysis, db
        )
        analysis["should_switch"] = should_switch
        analysis["switch_reason"] = reason

        return analysis

    def _calculate_avg_response_time(self, checks: List[HealthCheck]) -> Optional[float]:
        """计算平均响应时间"""
        response_times = [check.response_time for check in checks
                          if check.response_time is not None]
        return sum(response_times) / len(response_times) if response_times else None

    def _should_trigger_switch(
            self,
            service_id: int,
            analysis: Dict[str, any],
            db: Session
    ) -> tuple[bool, str]:
        """判断是否触发切换"""

        service = db.query(ServiceConfig).filter(ServiceConfig.id == service_id).first()
        if not service:
            return False, "Service not found"

        # 检查告警规则
        alert_rules = db.query(AlertRule) \
            .filter(AlertRule.service_id == service_id, AlertRule.is_active == True) \
            .all()

        for rule in alert_rules:
            if self._evaluate_alert_rule(rule, analysis):
                return True, f"Alert rule triggered: {rule.name}"

        # 默认故障检测逻辑
        if analysis["failure_rate"] >= 0.8 and analysis["total_checks"] >= 3:
            return True, "High failure rate detected"

        if analysis["latest_status"] in ["unhealthy", "timeout", "error"]:
            return True, f"Service status: {analysis['latest_status']}"

        return False, "Service appears healthy"

    def _evaluate_alert_rule(
            self,
            rule: AlertRule,
            analysis: Dict[str, any]
    ) -> bool:
        """评估告警规则"""

        if rule.rule_type == "health_check_failure":
            threshold = rule.threshold or {}
            min_failures = threshold.get("min_failures", 3)
            return analysis["failed_checks"] >= min_failures

        elif rule.rule_type == "response_time_threshold":
            threshold = rule.threshold or {}
            max_response_time = threshold.get("max_response_time_ms", 5000)
            avg_response = analysis["average_response_time"]
            return avg_response is not None and avg_response > max_response_time

        elif rule.rule_type == "failure_rate_threshold":
            threshold = rule.threshold or {}
            max_failure_rate = threshold.get("max_failure_rate", 0.5)
            return analysis["failure_rate"] > max_failure_rate

        return False

    def detect_failures(self, db: Session) -> Dict[int, Dict[str, any]]:
        """检测所有服务的故障"""
        services = db.query(ServiceConfig).filter(ServiceConfig.is_active == True).all()
        failures = {}

        for service in services:
            analysis = self.analyze_service_health(service.id, db)
            if analysis["should_switch"]:
                failures[service.id] = {
                    "service": service,
                    "analysis": analysis,
                    "reason": analysis["switch_reason"]
                }

        return failures

    def check_recovery_status(
            self,
            service_id: int,
            target_url: str,
            db: Session
    ) -> bool:
        """检查服务恢复状态"""

        # 获取目标服务的配置
        service = db.query(ServiceConfig).filter(ServiceConfig.id == service_id).first()
        if not service:
            return False

        # 临时修改URL进行健康检查
        original_url = service.primary_url if service.is_active else service.backup_url
        if service.is_active:
            service.primary_url = target_url
        else:
            service.backup_url = target_url

        try:
            # 执行健康检查
            import asyncio
            result = asyncio.run(health_checker.check_service_with_retry(service, db))
            return result[0]  # 返回健康状态
        finally:
            # 恢复原始URL
            if service.is_active:
                service.primary_url = original_url
            else:
                service.backup_url = original_url

    def get_service_uptime(
            self,
            service_id: int,
            days: int = 7,
            db: Session = None
    ) -> Dict[str, float]:
        """计算服务可用性"""

        end_time = datetime.utcnow()
        start_time = end_time - timedelta(days=days)

        checks = db.query(HealthCheck) \
            .filter(
            HealthCheck.service_id == service_id,
            HealthCheck.checked_at >= start_time,
            HealthCheck.checked_at <= end_time
        ) \
            .order_by(HealthCheck.checked_at) \
            .all()

        if not checks:
            return {
                "uptime_percentage": 0.0,
                "total_checks": 0,
                "healthy_checks": 0,
                "period_days": days
            }

        total_checks = len(checks)
        healthy_checks = sum(1 for check in checks if check.status == "healthy")
        uptime_percentage = (healthy_checks / total_checks) * 100

        return {
            "uptime_percentage": uptime_percentage,
            "total_checks": total_checks,
            "healthy_checks": healthy_checks,
            "period_days": days,
            "start_time": start_time,
            "end_time": end_time
        }


# 全局故障检测器实例
failure_detector = FailureDetector()

